#pragma once
#include "../Common/Common.hpp"
#include <boost/algorithm/string.hpp>
#include "../Common/Log.hpp"
namespace ns_index
{
    using namespace ns_log;
    // 正排节点
    struct DocInfo
    {
        uint64_t _id;
        std::string _title;
        std::string _content;
        std::string _url;
    };
    // 倒排节点
    struct InvertedElem
    {
        uint64_t _id;
        // 关键字
        std::string word;
        // 权重
        uint64_t weight;
    };
    // 词频权重结构
    struct WordCnt
    {
        int _title_cnt = 0;
        int _content_cnt = 0;
    };
    using InvertedList = std::vector<InvertedElem>;
    class Index
    {
    private:
        // 正排索引集
        std::vector<DocInfo> _forward_indexs;
        // 倒排索引集
        std::unordered_map<std::string, InvertedList> _inverted_indexs;

    public:
        Index(const Index&) = delete;
        Index& operator=(const Index&) = delete;
        Index()
        {}
        ~Index()
        {}
    public:
        // id--->文档内容
        DocInfo *GetForwardIndex(uint64_t id)
        {
            if (id >= _forward_indexs.size())
            {
                LOG(ERROR, "传入的正排索引参数为:%d,正排索引大小上限:%d\n", id, _forward_indexs.size());
                return nullptr;
            }
            return &_forward_indexs[id];
        }
        // 关键字--->倒排拉链
        InvertedList *GetInvertedIndex(const std::string &word)
        {
            auto pos = _inverted_indexs.find(word);
            if (pos == _inverted_indexs.end())
            {
                LOG(ERROR, "通过关键字%s在倒排索引中找不到!\n", word.c_str());
                return nullptr;
            }
            return &(pos->second);
        }
        // 构建索引
        bool BuildIndex(const std::string &path)
        {
            std::ifstream in(path, std::ios::in | std::ios::binary);
            if (!in.is_open())
            {
                LOG(FATAL, "文件路径不存在!无法构建索引!\n", path.c_str());
                return false;
            }
            int count = 0;
            std::string line;
            // 按行读取
            while (std::getline(in, line))
            {
                DocInfo *doc = BuildFrowardIndex(line);
                if (doc == nullptr)
                {
                    LOG(WARNING, "构建正排索引%s时失败!\n", line.c_str());
                    continue;
                }
                if (!BuildInvertedIndex(*doc))
                {
                    LOG(WARNING, "构建倒排索引时失败!,id = %d\n", doc->_id);
                    continue;
                }
                line.clear();
                //debug
                ++count;
                if(count%50 == 0)
                {
                    LOG(INFO,"当前已建立的索引文档: %d\n",count);
                }
            }
            return true;
        }

    private:
        // 构建正排索引
        DocInfo *BuildFrowardIndex(const std::string &input)
        {
            // Boost库的字符串切分
            std::vector<std::string> results;
            boost::split(results, input, boost::is_any_of(Util::SEP), boost::algorithm::token_compress_on);
            if (results.size() != 3)
            {
                LOG(WARNING, "切分文档内容异常,切分结果:实际[%d]期望[%d]!\n", results.size(), 3);
                return nullptr;
            }
            // results[0] --->title
            DocInfo doc;
            doc._title = results[0];
            // results[1] --->content
            doc._content = results[1];
            // results[2] --->url
            doc._url = results[2];
            // 构建ID
            doc._id = _forward_indexs.size();
            _forward_indexs.push_back(std::move(doc));
            return &_forward_indexs.back();
        }
        // 构建倒排索引
        bool BuildInvertedIndex(const DocInfo &doc)
        {
            // 权重表
            std::unordered_map<std::string, WordCnt> _weight_table;
            // 先分词
            // 切分title
            std::vector<std::string> title_words;
            Util::JiebalUtil::CutFromString(doc._title, &title_words);
            // 切分content
            std::vector<std::string> content_words;
            Util::JiebalUtil::CutFromString(doc._content, &content_words);
            // 采用的权重weight：词频决定！
            for (auto &word : title_words)
            {
                boost::to_lower(word);
                _weight_table[word]._title_cnt++;
            }
            // 遍历content
            for (auto &word : content_words)
            {
                boost::to_lower(word);
                _weight_table[word]._content_cnt++;
            }
            // 遍历权重表
            for (auto [word, cnts] : _weight_table)
            {
                InvertedElem elem;
                elem._id = doc._id;
                elem.word = word;
                elem.weight = cnts._title_cnt * 10 + cnts._content_cnt;
                InvertedList &list = _inverted_indexs[word];
                list.push_back(std::move(elem));
            }
            return true;
        }
    private:
        static Index* _instance;
        static std::mutex _mtx;
    public:
        static Index* GetInstance()
        {
            if(_instance == nullptr)
            {
                std::unique_lock<std::mutex>(_mtx);
                if(_instance == nullptr)
                {
                    _instance = new Index;
                }
            }
            return _instance;
        }
    };
    Index* Index::_instance = nullptr;
    std::mutex Index::_mtx;
}
